﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    class FileEnumerator
    {
        private readonly string[] _directoryPaths;
        private readonly CompareOperationState _state;

        public FileEnumerator(string[] directoryPaths, CompareOperationState state)
        {
            _directoryPaths = directoryPaths;
            _state = state;
        }

        public void EnumerateFiles()
        {
            Debug.WriteLine("FileEnumerator::EnumerateFiles start");
            _state.EnumerationState = CompareOperationState.State.Started;
            _state.ItemList.Clear();
            foreach (var directoryInfo in _directoryPaths.Select(path => new DirectoryInfo(path)))
                TraverseDirs(directoryInfo, _state.ItemList);
            _state.EnumerationState = CompareOperationState.State.Completed;
            Debug.WriteLine("FileEnumerator::EnumerateFiles end");
        }

        private static void TraverseDirs(DirectoryInfo dir, List<ItemInfo> items)
        {
            try
            {
                items.AddRange(dir.GetFiles().Select(iInfo => new ItemInfo(iInfo)));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            
            try
            {
                foreach (var iInfo in dir.GetDirectories()) TraverseDirs(iInfo, items);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // Note: up to now, this is the fastest implementation
        //public List<ItemInfo> EnumerateFiles1()
        //{
        //    var bag = new ConcurrentBag<ItemInfo>();
        //    foreach (var path in _directoryPaths)
        //    {
        //        var directoryInfo = new DirectoryInfo(path);
        //        TraverseDirs1(directoryInfo, bag);
        //    }

        //    var items = new List<ItemInfo>(bag);
        //    return items;
        //}

        //private static void TraverseDirs1(DirectoryInfo dir, ConcurrentBag<ItemInfo> items)
        //{
        //    try         // Avoid errors such as "Access Denied"
        //    {
        //        Parallel.ForEach(dir.GetDirectories(), subdir => TraverseDirs1(subdir, items));
        //    }
        //    catch (Exception ex)
        //    {
        //        MessageBox.Show(ex.Message);
        //    }

        //    try         // Avoid errors such as "Access Denied"
        //    {
        //        foreach (var iInfo in dir.GetFiles()) items.Add(new ItemInfo {Filename = iInfo.FullName, Size = iInfo.Length});
        //    }
        //    catch (Exception ex)
        //    {
        //        MessageBox.Show(ex.Message);
        //    }
        //}

        //public List<ItemInfo> EnumerateFiles2()
        //{
        //    var items = new List<ItemInfo>();
        //    foreach (var path in _directoryPaths)
        //    {
        //        TraverseDirs2(path, items);
        //    }

        //    return items;
        //}

        //private static void TraverseDirs2(string rootFolder, List<ItemInfo> fileList)
        //{
        //    var folderStack = new Stack<string>();
        //    var folderList = new Queue<string>();
        //    if (!Directory.Exists(rootFolder)) throw new ArgumentException();
            
        //    folderStack.Push(rootFolder);
        //    folderList.Enqueue(rootFolder);

        //    while (folderStack.Count > 0)
        //    {
        //        var currentFolder = folderStack.Pop();
        //        var files = Directory.GetFiles(currentFolder);
        //        fileList.AddRange(files.Select(file => new FileInfo(file)).Select(fileInfo => new ItemInfo {Filename = fileInfo.FullName, Size = fileInfo.Length}));

        //        try
        //        {
        //            var subfolders = Directory.GetDirectories(currentFolder);
        //            foreach (var subfolder in subfolders)
        //                folderStack.Push(subfolder);
        //        }
        //        catch (Exception ex)
        //        {
        //            MessageBox.Show(ex.Message);
        //            throw;
        //        }

        //    }
        //}

        //public List<ItemInfo> EnumerateFiles3()
        //{
        //    var items = new List<ItemInfo>();
        //    foreach (var path in _directoryPaths)
        //    {
        //        //var directoryInfo = new DirectoryInfo(path);
        //        TraverseDirs3(path, null, items, 0);
        //    }

        //    return items;
        //}

        //private static void TraverseDirs3(string rootFolder, List<string> folderList, List<ItemInfo> fileList, int level)
        //{
        //    if (!Directory.Exists(rootFolder)) throw new ArgumentException();
            
        //    if (folderList == null)
        //    {
        //        if (level == 0) folderList = new List<string>();
        //        else throw new Exception("folder list is null");
        //    }
            
        //    folderList.Add(rootFolder);

        //    var subfolders = Directory.GetDirectories(rootFolder);
        //    foreach (var subfolder in subfolders)
        //    {
        //        TraverseDirs3(subfolder, folderList, fileList, level + 1);
        //    }

        //    if (level == 0)
        //    {
        //        foreach (var files in folderList.Select(Directory.GetFiles))
        //        {
        //            fileList.AddRange(files
        //                    .Select(file => new FileInfo(file))
        //                    .Select(info => new ItemInfo {Filename = info.FullName, Size = info.Length}));
        //        }
        //    }
        //}

        //public List<ItemInfo> EnumerateFiles4()
        //{
        //    List<ItemInfo> items = null;
        //    foreach (var path in _directoryPaths)
        //    {
        //        TraverseDirs4(path, null, ref items, 0);
        //    }

        //    return items;
        //}

        //private static void TraverseDirs4(string rootFolder, List<string> folderList, ref List<ItemInfo> fileList, int level)
        //{
        //    if (!Directory.Exists(rootFolder)) throw new ArgumentException();

        //    if (folderList == null)
        //    {
        //        if (level == 0) folderList = new List<string>();
        //        else throw new Exception("folder list is null");
        //    }

        //    folderList.Add(rootFolder);

        //    var subfolders = Directory.GetDirectories(rootFolder);
        //    foreach (var subfolder in subfolders)
        //    {
        //        TraverseDirs4(subfolder, folderList, ref fileList, level + 1);
        //    }

        //    var bag = new ConcurrentBag<ItemInfo>();
            
        //    if (level == 0)
        //    {
        //        //var locker = new object();
        //        System.Threading.Tasks.Parallel.ForEach(folderList, 
        //            folder =>
        //                {
        //                    //var files = Directory.GetFiles(folder);
        //                    //var fileInfos = files.Select(f => new FileInfo(f));
        //                    var items = Directory.GetFiles(folder).Select(f => new FileInfo(f)).Select(f => new ItemInfo() { Filename = f.FullName, Size = f.Length });

        //                    //lock (locker)
        //                    //{
        //                    //    fileList.AddRange(items);
        //                    //}
        //                    foreach(var item in items )bag.Add(item);
        //                });
                
        //        fileList = new List<ItemInfo>(bag);
        //    }
        //}
    }
}
